#include <iostream>
#include <sstream>

#include "GraphHandler.hpp"
#include "IsoGraphEditDistance.hpp"

#include "dlib/svm.h"
#include "dlib/rand.h"


using namespace dlib;


using namespace std;
using namespace boost;





template<typename X>
void PrintVector(const std::vector<X> kVec)
{
    cout << "Vector: ";
    BOOST_FOREACH( const X& x, kVec )
        cout << x << ", ";
    cout << endl;
}





void TestDistance()
{
  typedef adjacency_list<listS, vecS, directedS,
    property<vertex_name_t, unsigned int,
    property<vertex_index_t, unsigned int> >,
    property<edge_name_t, unsigned int> > Graph;

  typedef graph_traits<Graph>::vertex_descriptor Vertex;
  typedef graph_traits<Graph>::edge_descriptor Edge;

  typedef property_map<Graph, vertex_name_t>::type VertexNameMap;
  typedef property_map<Graph, edge_name_t>::type EdgeNameMap;

  // Test maximum and unique variants on known graphs
  Graph graph_simple1, graph_simple2;
 // example_callback<Graph> user_callback(graph_simple1);

  VertexNameMap vname_map_simple1 = get(vertex_name, graph_simple1);
  VertexNameMap vname_map_simple2 = get(vertex_name, graph_simple2);

  // Graph that looks like a triangle
  put(vname_map_simple1, add_vertex(graph_simple1), 1);
  put(vname_map_simple1, add_vertex(graph_simple1), 2);
  put(vname_map_simple1, add_vertex(graph_simple1), 3);


  add_edge(0, 1, graph_simple1);
  add_edge(0, 2, graph_simple1);
  add_edge(1, 2, graph_simple1);

  std::cout << "First graph:" << std::endl;
  print_graph(graph_simple1);
  std::cout << std::endl;

  // Triangle with an extra vertex
  put(vname_map_simple2, add_vertex(graph_simple2), 1);
  put(vname_map_simple2, add_vertex(graph_simple2), 2);
  put(vname_map_simple2, add_vertex(graph_simple2), 3);
  put(vname_map_simple2, add_vertex(graph_simple2), 4);

  add_edge(0, 1, graph_simple2);
  add_edge(0, 2, graph_simple2);
  add_edge(1, 2, graph_simple2);
  add_edge(1, 3, graph_simple2);

    std::cout << "Second graph:" << std::endl;
  print_graph(graph_simple2);
  std::cout << std::endl;

  Graph graph_simple3 = graph_simple2;
  add_edge(2, 3, graph_simple3);

  Graph graph_simple4 = graph_simple1;
  clear_vertex(0, graph_simple4);
  remove_vertex(0, graph_simple4);

  print_graph(graph_simple4);
  std::cout << std::endl;

  cout << "Distance: " << IsoGraphEditDistance::GraphEditDistance(graph_simple1, graph_simple2) << endl;
  cout << "Distance: " << IsoGraphEditDistance::GraphEditDistance(graph_simple1, graph_simple3) << endl;
  cout << "Distance: " << IsoGraphEditDistance::GraphEditDistance(graph_simple2, graph_simple3) << endl;
  cout << "Distance: " << IsoGraphEditDistance::GraphEditDistance(graph_simple3, graph_simple4) << endl;

    Graph graph_simple5 = graph_simple4;
    add_edge(0, 2, graph_simple5);
    add_edge(0, 3, graph_simple5);
    add_edge(0, 4, graph_simple5);


    std::vector<Graph> gTraining;
    gTraining.push_back(graph_simple1);
    gTraining.push_back(graph_simple5);

    cout << "Graph simple 4" << endl;
    print_graph(graph_simple4);
    //PrintVector(IsoGraphEditDistance::GetGraphCoordinate(gTraining, graph_simple4));


    //VertexNameMap vname_map_simple4 = get(vertex_name, graph_simple4);
    //put(vname_map_simple4, add_vertex(graph_simple4), 2);
    //print_graph(graph_simple4);
   // PrintVector(IsoGraphEditDistance::GetGraphCoordinate(gTraining, graph_simple4));

    //add_edge(0, 2, graph_simple4);
    //print_graph(graph_simple4);
   // PrintVector(IsoGraphEditDistance::GetGraphCoordinate(gTraining, graph_simple4));


    Graph graph_simple6 = graph_simple4;
    remove_edge(0, 1, graph_simple6);

    cout << "Graph 6:" << endl;
    print_graph(graph_simple6);
    cout << "Distance: " << IsoGraphEditDistance::GraphEditDistance(graph_simple6, graph_simple6) << endl;

  //  PrintVector(IsoGraphEditDistance::GetGraphCoordinate(gTraining, graph_simple1));

}


int main()
{


    //TestDistance();
    //return 0;

    int iLoaded = GraphHandler::Instance().LoadAllGraphs("corpus/");


    cout << "Loaded in total " << iLoaded << " different graphs." << endl;

    set<string> toMerge;
    toMerge.insert("STAIR");
    toMerge.insert("LOBBY");

    set<string> blacklist;
    blacklist.insert("ELEC");


    //GraphHandler::Instance().Blacklist(blacklist);
    GraphHandler::Instance().FilterTransformMetaGraph(40, 10);
    GraphHandler::Instance().RemoveDuplicates();
    //GraphHandler::Instance().MergeVertices(toMerge, "N");
    GraphHandler::Instance().ListMetaVertices();
    GraphHandler::Instance().SaveMetaGraph("test2.dot");



    cout << "Test: << " << IsoGraphEditDistance::GraphEditDistance(GraphHandler::Instance().GetGraph(0), GraphHandler::Instance().GetGraph(0)) << endl;


  //cout.setf(ios::scientific,ios::floatfield);            // floatfield not set
  //cout.precision(15);
/*
    for(int iX = 1; iX < 200; iX++ )
    {
        cout << "Calculating " << iX << endl;
        cout << "Distance: " <<
        IsoGraphEditDistance::GraphEditDistance(GraphHandler::Instance().GetGraph(iX - 1), GraphHandler::Instance().GetGraph(iX)) << endl;
    }*/



    //print_graph(GraphHandler::Instance().GetGraph(4));
    //typedef matrix<double,75,1> sample_type;
    typedef matrix<double,0,1> sample_type;


    typedef radial_basis_kernel<sample_type> kernel_type;


    kcentroid<kernel_type> kc(kernel_type(0.1),0.01,8);

    kkmeans<kernel_type> test(kc);

    std::vector<sample_type> samples;
    std::vector<sample_type> initial_centers;

    sample_type m;


    ofstream fs;
    fs.open( "data" );

    int iN = GraphHandler::Instance().GetNumGraphs();

    for(int i = 0; i < GraphHandler::Instance().GetNumGraphs(); i++ )
    {
        //cout << "i: " << i << endl;
        for(int j = 0; j < GraphHandler::Instance().GetNumGraphs(); j++ )
        {
            m.set_size(iN);
           // cout << "j: " << j << endl;
            m(j) = IsoGraphEditDistance::GraphEditDistance(GraphHandler::Instance().GetGraph(i), GraphHandler::Instance().GetGraph(j));
            //cout << m(j) << endl;
            fs << m(j) << " ";
        }
        fs << endl;
        samples.push_back(m);
        //cout << "Done processing " << i << ":th sample" << endl;
    }

    fs.close();

    test.set_number_of_centers(5);

    // You need to pick some initial centers for the k-means algorithm.  So here
    // we will use the dlib::pick_initial_centers() function which tries to find
    // n points that are far apart (basically).
    pick_initial_centers(5, initial_centers, samples, test.get_kernel());

    // now run the k-means algorithm on our set of samples.
    test.train(samples,initial_centers);

    // now loop over all our samples and print out their predicted class.  In this example
    // all points are correctly identified.
    for (unsigned long i = 0; i < samples.size(); ++i)
    {
        cout << test(samples[i]) << " ";
    }


    return 0;
}
